#! /usr/bin/env python

# -*- coding: UTF-8 -*-
# ==============================================================================
# Filename : CheckUpdate.py
# Purpose  : 
# Author   : Yohann Li (lyh001686@sina.com)
# Date     : 2020-09-02 16:47
# ==============================================================================
import json
import os
import sys

from PyQt5.QtCore import Qt, QPoint, QThread, pyqtSignal
from PyQt5.QtGui import QPixmap
from requests import get, post, exceptions
from PyQt5.QtWidgets import QLabel, QApplication, QGridLayout, QPushButton, QDialog, \
    QDialogButtonBox, QTextBrowser, QProgressBar
from conf.configuration import URL, logger, APPNAME, APPPIC


def check_update():
    r = get(URL, timeout=1)
    return json.loads(r.content)


class CheckUpdate(QDialog):
    def __init__(self, current_v, silent: bool = False, *args, **kwargs):
        super(CheckUpdate, self).__init__(*args, **kwargs)
        self.silent = silent
        try:
            self.property_ = check_update()
            logger.debug(f"{self.property_}")
            self.cur_version = current_v
            self.release_notes = self.property_.get('note', 'Fix some bugs...')
            self.new_version = self.property_.get("version", "")
            logger.info(f"cur_version {self.cur_version}, new_version {self.new_version}")
            if self.cur_version != self.new_version and bool(current_v):
                self.silent = False
                self._remind_update()
            else:
                self._up_to_date()
        except exceptions.ConnectionError as e:
            logger.error(e)
            self._connection_error()
        self.move(int((QApplication.desktop().screen().width() - self.width()) / 2),
                  int((QApplication.desktop().screen().height() - self.height()) / 3))
        self.setWindowFlag(Qt.FramelessWindowHint)
        self.setWindowOpacity(0.9)
        self.show_(self.silent)

    def _up_to_date(self):
        logger.debug("You are up-to-date!")
        icon_label = self._init_logo()
        title = CustLabel(f"You are up-to-date!")
        title.setStyleSheet('font-weight: bold')
        msg = CustLabel(f'{APPNAME} {self.new_version} is currently the newest version available.')
        btn_ok = QPushButton('OK')
        btn_ok.clicked.connect(self.close)
        btn_box = QDialogButtonBox()
        btn_box.addButton(btn_ok, QDialogButtonBox.AcceptRole)
        lay = QGridLayout(self)
        lay.addWidget(icon_label, 0, 0, 2, 1)
        lay.addWidget(title, 0, 1, 1, 5)
        lay.addWidget(msg, 1, 1, 1, 5)
        # lay.setSpacing(50)
        lay.addWidget(btn_box, 2, 4)
        self.setFixedWidth(400)

    def _remind_update(self):
        logger.debug("Remind for updating.")
        icon_label = self._init_logo()
        title = CustLabel(f"A new version of Reader is available!")
        title.setStyleSheet('font-weight: bold')
        msg = CustLabel(f"{APPNAME} {self.new_version} is now available while you have {self.cur_version}."
                        f" Would you like to download it now?")

        notes_label = CustLabel(f"Release Notes:")
        notes_ = QTextBrowser()
        notes_.setOpenExternalLinks(True)
        notes_.setHtml(self.release_notes)
        btn_ok = QPushButton('Install Update')
        btn_ok.clicked.connect(self.close)
        btn_ok.clicked.connect(self.install_update)
        btn_ok.clicked.connect(self._downloading)
        btn_cancel = QPushButton('Cancel')
        btn_cancel.clicked.connect(self.close)
        btn_box = QDialogButtonBox()
        btn_box.addButton(btn_ok, QDialogButtonBox.AcceptRole)
        btn_box.addButton(btn_cancel, QDialogButtonBox.RejectRole)
        lay = QGridLayout(self)
        lay.addWidget(icon_label, 1, 1, 2, 1)
        lay.addWidget(title, 1, 2, 1, 4)
        lay.addWidget(msg, 2, 2, 1, 4)
        lay.addWidget(notes_label, 3, 2, 1, 4)
        lay.addWidget(notes_, 4, 2, 1, 4)
        lay.addWidget(btn_box, 5, 4, 1, 2)
        self.setFixedWidth(600)

    def _connection_error(self):
        logger.debug("Connect alert")
        icon_label = self._init_logo()
        title = CustLabel(f"Connection Alert!")
        title.setStyleSheet('font-weight: bold')
        msg = CustLabel(f"A Connection to the application server could not be established.\n"
                        f"Please connect to network or mail to lyh001686@sina.com")
        btn_ok = QPushButton('OK')
        btn_ok.clicked.connect(self.close)
        btn_box = QDialogButtonBox()
        btn_box.addButton(btn_ok, QDialogButtonBox.AcceptRole)
        lay = QGridLayout(self)
        lay.addWidget(icon_label, 1, 1, 2, 1)
        lay.addWidget(title, 1, 2, 1, 4)
        lay.addWidget(msg, 2, 2, 2, 4)
        lay.addWidget(btn_box, 4, 5, 1, 1)
        self.setFixedWidth(400)

    def _downloading(self):
        logger.debug("Downloading updates...")
        download_window = CustDialog()
        icon_label = self._init_logo()
        title = CustLabel(f"Downloading update...")
        title.setStyleSheet('font-weight: bold')
        self.pro_bar_ = QProgressBar()
        self.pro_rate = CustLabel()
        btn_ok = QPushButton('Cancel')
        btn_ok.clicked.connect(download_window.close)
        btn_box = QDialogButtonBox()
        btn_box.addButton(btn_ok, QDialogButtonBox.RejectRole)
        lay = QGridLayout(download_window)
        lay.addWidget(icon_label, 1, 1, 2, 1)
        lay.addWidget(title, 1, 2, 1, 4)
        lay.addWidget(self.pro_bar_, 2, 2, 1, 4)
        lay.addWidget(self.pro_rate, 3, 2, 1, 4)
        lay.addWidget(btn_box, 4, 5, 1, 1)
        download_window.setFixedWidth(400)
        download_window.move(int((QApplication.desktop().screen().width() - download_window.width()) / 2),
                             int((QApplication.desktop().screen().height() - download_window.height()) / 3))
        download_window.exec_()

    def install_update(self):
        self.file_name = self.property_.get('app')
        self.download_thread = DownloadThread(self, file_name=self.file_name)
        self.download_thread.pro_rate_sig.connect(self.update_pro_stat)
        self.download_thread.start()

    def update_pro_stat(self, v: tuple):
        """v = (value, Max)"""
        v_, max_ = v
        # logger.debug(f"{v}")
        self.pro_bar_.setMaximum(max_)
        self.pro_bar_.setValue(v_)
        self.pro_rate.setText(f"{self.strfsize(v_)} of {self.strfsize(max_)}")
        if v_ == max_:  # 下载完后执行的动作
            d_name = os.path.splitext(self.file_name)[0]
            from tarfile import TarFile
            with TarFile.open(f"/tmp/{self.file_name}") as tf:
                tf.extractall(path=os.path.expanduser(f'~/Desktop/{d_name}'))
            cmd = "open " + os.path.expanduser(f'~/Desktop/{d_name}')
            logger.debug(f"run CMD: {cmd}")
            os.popen(cmd)

    def strfsize(self, size: int):
        if size > 1024 ** 3:
            size = f'{size / (1024 ** 3):<6.2f} GB'
        elif size > 1024 ** 2:
            size = f'{size / (1024 ** 2):<6.2f} MB'
        else:
            size = f'{size / (1024):<6.2f} Bytes'
        return size

    def ok_clicked(self):
        """OK to update app"""
        logger.debug(f"ok")

    def cancel_clicked(self):
        logger.debug(f"Cancel")
        self.close()

    def _init_logo(self):
        icon_label = QLabel()
        icon_ = QPixmap(APPPIC)
        # icon_ = icon_.scaled(96, 96, Qt.KeepAspectRatio)
        icon_label.setPixmap(icon_)
        return icon_label

    def show_(self, silent: bool = False):
        logger.debug(f"Show check for updates: {silent}")
        self.show() if not silent else ''

    def mousePressEvent(self, event):
        self.currentPos = event.pos()

    def mouseMoveEvent(self, event):
        self.move(QPoint(self.pos() + event.pos() - self.currentPos))


class DownloadThread(QThread):
    pro_rate_sig = pyqtSignal(tuple)

    def __init__(self, *args, **kwargs):
        super(DownloadThread, self).__init__(*args)
        logger.debug(f"Start download thread")
        self.file_name = kwargs.get('file_name')

    def run(self) -> None:
        self.download_file(self.file_name)

    def download_file(self, file_name):
        try:
            logger.debug(f"Start to download {file_name}")
            r = post(URL, json={'app': file_name}, stream=True)
            size_ = int(r.headers.get('Content-Length', 0))
            BFSZ = 1024
            offset = 0
            with open(f'/tmp/{file_name}', 'wb') as f:
                for chunk in r.iter_content(chunk_size=BFSZ):
                    if not chunk:
                        logger.info(f"No chunk, end to download.")
                        return
                    f.write(chunk)
                    offset += len(chunk)
                    self.pro_rate_sig.emit((offset, size_))

        except exceptions.ConnectionError as e:
            logger.error(e)


class CustLabel(QLabel):
    def __init__(self, *args, **kwargs):
        super(CustLabel, self).__init__(*args, **kwargs)
        self.setWordWrap(True)
        self.setTextInteractionFlags(Qt.TextBrowserInteraction)
        # self.setStyleSheet('border: 1px solid black')


class CustDialog(QDialog):
    def __init__(self, *args, **kwargs):
        super(CustDialog, self).__init__(*args, **kwargs)
        self.setWindowFlag(Qt.FramelessWindowHint)
        self.setWindowOpacity(0.9)

    def mousePressEvent(self, event):
        self.currentPos = event.pos()

    def mouseMoveEvent(self, event):
        self.move(QPoint(self.pos() + event.pos() - self.currentPos))

if __name__ == '__main__':
    app = QApplication(sys.argv)
    t = CheckUpdate(current_v='1.0')
    t.show()
    sys.exit(app.exec_())
